﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Muel.TypeScriptFacade.Core.Configuration
{
    public partial class TypeScriptFacadeSettings
    {
        public static TypeScriptFacadeSettings Default
        {
            get
            {
                return new TypeScriptFacadeSettings()
                {
                    ServiceFacadeName = "ServiceFacade",
                    ChangeTracking = new TypeScriptFacadeChangeTracking()
                    {
                        Enabled = true,
                    },
                    Exclusions = new List<string>()
                };
            }
        }

        public IEnumerable<ValidationError> Validate()
        {
            // validate characters in service facade name
            if (!ContainsOnlyValidTypeScriptMethodCharacters(this.ServiceFacadeName))
            {
                yield return new ValidationError(ValidationErrorType.InvalidCharactersInFacadeName);
            }

            // validate characters in snapshot name
            if (!ContainsOnlyValidTypeScriptMethodCharacters(this.ChangeTracking.SnapshotMethodName))
            {
                yield return new ValidationError(ValidationErrorType.InvalidCharactersInSnapshotName);
            }

            // validate characters in hasChanges name
            if (!ContainsOnlyValidTypeScriptMethodCharacters(this.ChangeTracking.HasChangesMethodName))
            {
                yield return new ValidationError(ValidationErrorType.InvalidCharactersInHasChangesName);
            }

            // check hasChanges name is different to snapshot name
            if (this.ChangeTracking.HasChangesMethodName == this.ChangeTracking.SnapshotMethodName)
            {
                yield return new ValidationError(ValidationErrorType.SnapshotNameEqualsHasChangesName);
            }

            // check for reserved words in service facade name
            if (IsReservedWord(this.ServiceFacadeName))
            {
                yield return new ValidationError(ValidationErrorType.FacadeNameIsReserved);
            }
            
            // check for reserved words in snapshot method name
            if (IsReservedWord(this.ChangeTracking.SnapshotMethodName)) 
            {
                yield return new ValidationError(ValidationErrorType.SnapshotMethodNameIsReserved);
            }

            // check for reserved words in hasChanges method name
            if (IsReservedWord(this.ChangeTracking.HasChangesMethodName))
            {
                yield return new ValidationError(ValidationErrorType.HasChangesMethodNameIsReserved);
            }

            // check for reserved words in revert method name
            if (IsReservedWord(this.ChangeTracking.RevertMethodName))
            {
                yield return new ValidationError(ValidationErrorType.RevertMethodNameIsReserved);
            }

            // if AutoSnapshot = true check that Enabled = true
            if (ChangeTracking.AutoSnapshotReturnedObjects && !ChangeTracking.Enabled)
            {
                yield return new ValidationError(ValidationErrorType.UnexpectedAutoSnapshot);
            }
        }

        private bool ContainsOnlyValidTypeScriptMethodCharacters(string input)
        {
            return Regex.IsMatch(input, "^[a-zA-Z0-9_]+$");
        }

        internal static string[] TypeScriptReservedWords = new string[]
        {
            "constructor",
            "this",
            "var",
            "new",
            "object",
            "class",
            "module"
        };

        private bool IsReservedWord(string input)
        {
            return TypeScriptReservedWords.Contains(input);
        }

        internal bool ShouldTreatAsAny(Type type)
        {
            if (type.FullName == null)
            {
                return false;
            }
            return this.TreatAsAny == null ||                 
                this.TreatAsAny.Any(pattern => Regex.IsMatch(type.FullName, pattern));
        }
    }
}
